use crate::json::parser::Listener;
use crate::json::parser::Token;


pub struct FormatListener {
    pub builder: String,
    pub pretty: bool,
    pub scope: u32,
    pub line_start: bool,
}

const EMPTY: char = ' ';
const LINE: char = '\n';

impl Listener for FormatListener {
    fn enter_null_node(&mut self, token: &Token) {
        self.append(&token.value);
    }

    fn enter_string_node(&mut self, token: &Token) {
        let mut val = String::new();
        val.push_str("\"");
        val.push_str(&token.value);
        val.push_str("\"");
        self.append(&val);
    }

    fn enter_number_node(&mut self, token: &Token) {
        self.append(&token.value)
    }

    fn enter_identifier_node(&mut self, token: &Token) {
        self.append(&token.value)
    }

    fn enter_true_node(&mut self, token: &Token) {
        self.append(&token.value)
    }

    fn enter_false_node(&mut self, token: &Token) {
        self.append(&token.value)
    }

    fn enter_comment_node(&mut self, token: &Token) {
        let mut val = String::from("//");
        val.push_str(&token.value);
        self.append(&val);
    }

    fn enter_object_node(&mut self, token: &Token) {
        self.append(&token.value);
        self.new_line();
        self.enter_scope();
    }

    fn exit_object_node(&mut self, token: &Token) {
        self.exit_scope();
        self.new_line();
        self.append(&token.value);
    }

    fn enter_array_node(&mut self, token: &Token) {
        self.append(&token.value);
        self.new_line();
        self.enter_scope();
    }

    fn exit_array_node(&mut self, token: &Token) {
        self.exit_scope();
        self.new_line();
        self.append(&token.value);
    }

    fn enter_comma_node(&mut self, token: &Token) {
        self.append(&token.value);
        self.new_line();
    }

    fn enter_colon_node(&mut self, token: &Token) {
        self.append(&token.value);
    }

    fn get_value<'a>(&self) -> &String {
        return &self.builder;
    }
}

impl FormatListener {
    fn new_line(&mut self) {
        if !self.pretty {
            return;
        }
        self.line_start = true;
        self.builder.push(LINE);
    }

    fn padding(&mut self) {
        if !self.pretty {
            return;
        }
        if self.line_start {
            let current_indent = self.scope * 4;
            for _ in 0..current_indent {
                self.builder.push(EMPTY);
            }
        }
    }

    fn enter_scope(&mut self) {
        self.scope += 1;
    }
    fn exit_scope(&mut self) {
        self.scope -= 1;
    }

    fn append(&mut self, value: &str) {
        self.padding();
        self.builder.push_str(value);
        self.line_start = false;
    }
}